1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static java.util.Arrays.asList;
20  
21  import com.google.common.annotations.GwtIncompatible;
22  import com.google.common.base.Objects;
23  import com.google.common.base.Predicate;
24  import com.google.common.base.Predicates;
25  import com.google.common.collect.testing.features.CollectionFeature;
26  import com.google.common.collect.testing.features.CollectionSize;
27  import com.google.common.collect.testing.google.MultisetTestSuiteBuilder;
28  import com.google.common.collect.testing.google.SortedMultisetTestSuiteBuilder;
29  import com.google.common.collect.testing.google.TestStringMultisetGenerator;
30  
31  import junit.framework.Test;
32  import junit.framework.TestCase;
33  import junit.framework.TestSuite;
34  
35  import java.util.ArrayList;
36  import java.util.Collections;
37  import java.util.List;
38  
39  /**
40   * Collection tests on wrappers from {@link Multisets}.
41   *
42   * @author Jared Levy
43   */
44  @GwtIncompatible("suite") // TODO(cpovirk): set up collect/gwt/suites version
45  public class MultisetsCollectionTest extends TestCase {
46    public static Test suite() {
47      TestSuite suite = new TestSuite();
48  
49      suite.addTest(MultisetTestSuiteBuilder.using(
50          unmodifiableMultisetGenerator())
51          .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
52              CollectionFeature.SERIALIZABLE,
53              CollectionFeature.ALLOWS_NULL_QUERIES)
54          .named("Multisets.unmodifiableMultiset[LinkedHashMultiset]")
55          .createTestSuite());
56  
57      suite.addTest(SortedMultisetTestSuiteBuilder.using(unmodifiableSortedMultisetGenerator())
58          .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
59              CollectionFeature.ALLOWS_NULL_QUERIES)
60          .named("Multisets.unmodifiableMultiset[TreeMultiset]")
61          .createTestSuite());
62  
63      suite.addTest(MultisetTestSuiteBuilder.using(unionGenerator())
64          .withFeatures(CollectionSize.ANY,
65              CollectionFeature.ALLOWS_NULL_VALUES)
66          .named("Multisets.union")
67          .createTestSuite());
68  
69      suite.addTest(MultisetTestSuiteBuilder.using(intersectionGenerator())
70          .withFeatures(CollectionSize.ANY,
71              CollectionFeature.ALLOWS_NULL_VALUES,
72              CollectionFeature.KNOWN_ORDER)
73          .named("Multisets.intersection")
74          .createTestSuite());
75  
76      suite.addTest(MultisetTestSuiteBuilder.using(sumGenerator())
77          .withFeatures(CollectionSize.ANY,
78              CollectionFeature.ALLOWS_NULL_VALUES)
79          .named("Multisets.sum")
80          .createTestSuite());
81  
82      suite.addTest(MultisetTestSuiteBuilder.using(differenceGenerator())
83          .withFeatures(CollectionSize.ANY,
84              CollectionFeature.ALLOWS_NULL_VALUES,
85              CollectionFeature.KNOWN_ORDER)
86          .named("Multisets.difference")
87          .createTestSuite());
88      
89      suite.addTest(MultisetTestSuiteBuilder.using(filteredGenerator())
90          .withFeatures(CollectionSize.ANY,
91              CollectionFeature.ALLOWS_NULL_VALUES,
92              CollectionFeature.KNOWN_ORDER,
93              CollectionFeature.SUPPORTS_ADD,
94              CollectionFeature.SUPPORTS_REMOVE)
95          .named("Multiset.filter[Multiset, Predicate]")
96          .createTestSuite());
97  
98      return suite;
99    }
100 
101   private static TestStringMultisetGenerator unmodifiableMultisetGenerator() {
102     return new TestStringMultisetGenerator() {
103       @Override protected Multiset<String> create(String[] elements) {
104         return Multisets.unmodifiableMultiset(
105             LinkedHashMultiset.create(asList(elements)));
106       }
107       @Override public List<String> order(List<String> insertionOrder) {
108         List<String> order = new ArrayList<String>();
109         for (String s : insertionOrder) {
110           int index = order.indexOf(s);
111           if (index == -1) {
112             order.add(s);
113           } else {
114             order.add(index, s);
115           }
116         }
117         return order;
118       }
119     };
120   }
121 
122   private static TestStringMultisetGenerator unmodifiableSortedMultisetGenerator() {
123     return new TestStringMultisetGenerator() {
124       @Override protected Multiset<String> create(String[] elements) {
125         return Multisets.unmodifiableSortedMultiset(
126             TreeMultiset.create(asList(elements)));
127       }
128       @Override public List<String> order(List<String> insertionOrder) {
129         Collections.sort(insertionOrder);
130         return insertionOrder;
131       }
132     };
133   }
134 
135   private static TestStringMultisetGenerator unionGenerator() {
136     return new TestStringMultisetGenerator() {
137       @Override
138       protected Multiset<String> create(String[] elements) {
139         Multiset<String> multiset1 = LinkedHashMultiset.create();
140         Multiset<String> multiset2 = LinkedHashMultiset.create();
141         for (int i = 0; i < elements.length; i++) {
142           String element = elements[i];
143           if (multiset1.contains(element) ||
144               multiset2.contains(element)) {
145             // add to both; the one already containing it will have more
146             multiset1.add(element);
147             multiset2.add(element);
148           } else if (i % 2 == 0) {
149             multiset1.add(elements[i]);
150           } else {
151             multiset2.add(elements[i]);
152           }
153         }
154         return Multisets.union(multiset1, multiset2);
155       }
156     };
157   }
158 
159   private static TestStringMultisetGenerator intersectionGenerator() {
160     return new TestStringMultisetGenerator() {
161       @Override protected Multiset<String> create(String[] elements) {
162         Multiset<String> multiset1 = LinkedHashMultiset.create();
163         Multiset<String> multiset2 = LinkedHashMultiset.create();
164         multiset1.add("only1");
165         multiset2.add("only2");
166         for (int i = 0; i < elements.length; i++) {
167           multiset1.add(elements[i]);
168           multiset2.add(elements[elements.length - 1 - i]);
169         }
170         if (elements.length > 0) {
171           multiset1.add(elements[0]);
172         }
173         if (elements.length > 1) {
174           /*
175            * When a test requests a multiset with duplicates, our plan of
176            * "add an extra item 0 to A and an extra item 1 to B" really means
177            * "add an extra item 0 to A and B," which isn't what we want.
178            */
179           if (!Objects.equal(elements[0], elements[1])) {
180             multiset2.add(elements[1], 2);
181           }
182         }
183         return Multisets.intersection(multiset1, multiset2);
184       }
185     };
186   }
187 
188   private static TestStringMultisetGenerator sumGenerator() {
189     return new TestStringMultisetGenerator() {
190       @Override protected Multiset<String> create(String[] elements) {
191         Multiset<String> multiset1 = LinkedHashMultiset.create();
192         Multiset<String> multiset2 = LinkedHashMultiset.create();
193         for (int i = 0; i < elements.length; i++) {
194           // add to either; sum should contain all
195           if (i % 2 == 0) {
196             multiset1.add(elements[i]);
197           } else {
198             multiset2.add(elements[i]);
199           }
200         }
201         return Multisets.sum(multiset1, multiset2);
202       }
203     };
204   }
205 
206   private static TestStringMultisetGenerator differenceGenerator() {
207     return new TestStringMultisetGenerator() {
208       @Override protected Multiset<String> create(String[] elements) {
209         Multiset<String> multiset1 = LinkedHashMultiset.create();
210         Multiset<String> multiset2 = LinkedHashMultiset.create();
211         multiset1.add("equalIn1");
212         multiset1.add("fewerIn1");
213         multiset2.add("equalIn1");
214         multiset2.add("fewerIn1", 3);
215         multiset2.add("onlyIn2", 2);
216         for (int i = 0; i < elements.length; i++) {
217           // add 1 more copy of each element to multiset1 than multiset2
218           multiset1.add(elements[i], i + 2);
219           multiset2.add(elements[i], i + 1);
220         }
221         return Multisets.difference(multiset1, multiset2);
222       }
223     };
224   }
225   
226   private static final Multiset<String> ELEMENTS_TO_FILTER_OUT = ImmutableMultiset.of(
227       "foobar", "bazfoo", "foobar", "foobar");
228   
229   private static final Predicate<String> PREDICATE = 
230       Predicates.not(Predicates.in(ELEMENTS_TO_FILTER_OUT));
231   
232   private static TestStringMultisetGenerator filteredGenerator() {
233     return new TestStringMultisetGenerator() {
234       @Override
235       protected Multiset<String> create(String[] elements) {
236         Multiset<String> multiset = LinkedHashMultiset.create();
237         Collections.addAll(multiset, elements);
238         multiset.addAll(ELEMENTS_TO_FILTER_OUT);
239         return Multisets.filter(multiset, PREDICATE);
240       }
241 
242       @Override
243       public List<String> order(List<String> insertionOrder) {
244         return Lists.newArrayList(LinkedHashMultiset.create(insertionOrder));
245       }
246     };
247   }
248 }